#region Using directives

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Ndo
{
    internal abstract class BoxFactory<T>
    {
        internal const string TargetFieldName = "__target";
        static BoxFactory()
        {
            if (typeof(T).IsNotPublic)
                throw new NotSupportedException();
            if (typeof(T).IsInterface)
                BaseConstructor = BoxHelper.ObjectConstructor;
            else
            {
                if (typeof(T).IsSealed)
                    throw new NotSupportedException();
                ConstructorInfo[] cis = typeof(T).GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                for (int i = cis.Length - 1; i >= 0; i--)
                {
                    BaseConstructor = cis[i];
                    if (BaseConstructor.GetParameters().Length == 0)
                    {
                        //Check All Public Methods are virtual
                        foreach (MethodInfo mi in typeof(T).GetMethods(BindingFlags.Public | BindingFlags.Instance))
                            if (!mi.Name.Equals("GetType") && !mi.IsVirtual)
                                throw new NotSupportedException();
                        break;
                    }
                    BaseConstructor = null;
                }
                if (BaseConstructor == null)
                    throw new NotSupportedException();
            }
        }
        internal static readonly ConstructorInfo BaseConstructor;
        protected BoxFactory()
        {
        }
        internal ConstructorInfo CI;
        protected TypeBuilder tb;
        protected FieldBuilder fb;
        protected void ImplementMethodInvoke(MethodAttributes ma, MethodInfo mi)
        {
            Type[] ts = BoxHelper.GetParamsTypes(mi.GetParameters());
            MethodBuilder mb = tb.DefineMethod(mi.Name, ma, mi.ReturnType, ts);
            ILGenerator g = mb.GetILGenerator();
            g.Emit(OpCodes.Ldarg_0);
            g.Emit(OpCodes.Ldfld, fb);
            switch (ts.Length)
            {
                case 0:
                    break;
                case 1:
                    g.Emit(OpCodes.Ldarg_1);
                    break;
                case 2:
                    g.Emit(OpCodes.Ldarg_1);
                    g.Emit(OpCodes.Ldarg_2);
                    break;
                default:
                    g.Emit(OpCodes.Ldarg_1);
                    g.Emit(OpCodes.Ldarg_2);
                    g.Emit(OpCodes.Ldarg_3);
                    for (byte i = 4; i <= ts.Length; i++)
                        g.Emit(OpCodes.Ldarg_S, i);
                    break;
            }
            g.Emit(OpCodes.Callvirt, mi);
            g.Emit(OpCodes.Ret);
            tb.DefineMethodOverride(mb, mi);
        }
    }

}
